#ifndef TTY_H
#define TTY_H

#include <jimix/Globals.h>
#include <jimix/MonitorDriver.h>
#include <jimix/resource.h>
#include <string.h>
#include <config.h>

typedef u32int cc_t;
typedef u32int speed_t;
typedef u32int tcflag_t;

#define NCCS 13 // The number of control characters.
#define VEOF   4       /* also VMIN -- thanks, AT&T */
#define VEOL   5       /* also VTIME -- thanks again */
#define VERASE 2
#define VINTR  0
#define VKILL  3
#define VMIN   4       /* also VEOF */
#define VQUIT  1
#define VSUSP  10
#define VTIME  5       /* also VEOL */
#define VSTART 11
#define VSTOP  12

// Defines for the c_lflag attribute of termios.
#define ECHO   0000010
#define ECHOE  0000020
#define ECHOK  0000040
#define ECHONL 0000100
#define ICANON 0000002
#define IEXTEN 0000400 /* anybody know *what* this does?! */
#define ISIG   0000001
#define NOFLSH 0000200
#define TOSTOP 0001000

typedef struct termios
{
  tcflag_t c_iflag;   ///< Has useless properties - ignored.
  tcflag_t c_oflag;   ///< Has useless properties - ignored.
  tcflag_t c_cflag;   ///< Has useless properties - ignored.
  tcflag_t c_lflag;   ///< ECHO,ECHOE,ECHONL recognised.
  cc_t     c_cc[NCCS];
} termios_t;

/**
  RemoteCall Interface
**/
namespace Tty_IF
{
  const int tcsetattr  =0;
  const int tcgetattr  =1;
}

/**
  Abstract class describing a teletype.
**/
class Tty : public Callable
{
public:
  Tty();
  ~Tty();
  
  /**
    Writes a character string to the TTY.
  **/
  virtual void write(char *str);
  
  /**
    Writes a character to the TTY.
  **/
  virtual void write(char c);
  
  /**
    Reads a character from the TTY.
  **/
  virtual char read();
  
  virtual void ungetc(char c)
  {}
  
  virtual void setActive(bool b);
  
  virtual bool getActive()
  {
    return false;
  }
  
  virtual bool isLocal()
  {
    return false;
  }
  
  int tcsetattr(termios_t *props)
  {
    memcpy(&properties, props, sizeof(termios_t));
    return 0;
  }
  int tcgetattr(termios_t *t)
  {
    memcpy(t, &properties, sizeof(termios_t));
    return 0;
  }
  
  u32int fileRead(u32int impl, u32int offset, u32int size,
                  u8int *buffer);
  u32int fileWrite(u32int impl, u32int offset, u32int size,
                   u8int *buffer);
  
  /**
    We are a remote resource.
  **/
  u32int resource;
private:
  /**
    The properties of this terminal.
  **/
  termios_t properties;
};

/**
  Concrete class describing a teletype that is designed to be shown
  on screen. The implementation uses a framebuffer as opposed to a linear
  buffer.
**/
class LocalTty : public Tty
{
public:
  /**
    Generic constructor/destructor
  **/
  LocalTty(u32int c, u32int r, char *desc);
  ~LocalTty();
  
  /**
    Writes a character to the local framebuffer, and, 
    if the tty is currently active, refreshes the screen.
  **/
  void write(char c);
  
  void write(char *str);
  
  /**
    Reads a character from the keyboard buffer. Returns -1 if none available.
  **/
  char read();
  
  /**
    Sets whether this TTY is in charge of the screen. A value of true
    will also trigger a refresh.
  **/
  void setActive(bool b);
  bool getActive()
  {
    return isActive;
  }
  
  virtual bool isLocal()
  {
    return true;
  }
  
  /**
    Informs the tty that keyboard input has been recieved, and instructs
    it to add the given character to it's input buffer.
  **/
  void inputReceived(char c);
  
  /**
    Performs an error-correcting backspace.
  **/
  void backspace();
  
  
  virtual void ungetc(char c);
  
private:
  /**
    Write a description of the current tty in the top-right corner.
  **/
  void writeTtyDescription();  
  
  /**
    Refresh the screen.
  **/
  void refresh();
  
  /**
    Scroll the buffer up one line.
  **/
  void scroll();
  
  /**
    The framebuffer.
  **/
  u16int *framebuffer;
  
  /**
    True if this TTY is currently in charge of the screen.
  **/
  bool isActive;
  
  /**
    Cached monitor driver instance.
  **/
  u32int monitor;
  
  /**
    The cursor position
  **/
  u32int cursorX, cursorY;
  
  /**
    The width and height of the screen.
  **/
  u32int C, R;
  
  /**
    The input buffer.
  **/
  char buffer[256];
  int bufStart, bufEnd;
  
  /**
    Description of this tty, e.g. "TTY1"
  **/
  char *description;
};

/**
  Concrete class describing a teletype that is designed to be written
  over a serial link.
**/
class SerialTty : public Tty
{
  public:
  /**
    Generic constructor/destructor
  **/
  SerialTty(char *desc, u16int port);
  ~SerialTty();

  /**
    Writes a character to the local framebuffer, and, 
    if the tty is currently active, refreshes the screen.
  **/
  void write(char c);

  /**
    Reads a character from the keyboard buffer. Returns -1 if none available.
  **/
  char read();

  /**
    Informs the tty that serial input has been recieved, and instructs
    it to add the given character to it's input buffer.
  **/
  void inputReceived(char c);

  virtual void ungetc(char c);
  
private:
  /**
    The input buffer.
  **/
  char buffer[256];
  int bufStart, bufEnd;

  /**
    Description of this tty, e.g. "TTY1"
  **/
  char *description;
  
  /**
    The serial port base number.
  **/
  u16int port;
};

#endif
